/** biome-ignore-all lint/suspicious/noExplicitAny: allow any */
import fs from "node:fs";
import {
  CommunicationWay,
  ServiceHandler,
  ServiceRegister,
} from "@main/shared/reflect";
import { TYPES } from "@main/shared/types";
import { triplitClient } from "@main/triplit/client";
import { extractErrorMessage } from "@main/utils/error-utils";
import logger from "@shared/logger/main-logger";
import { schema } from "@shared/triplit/schema";
import type { CollectionNameFromModels } from "@triplit/client";
import { dialog } from "electron";
import { injectable } from "inversify";
import semver from "semver";
import packageJSON from "../../../package.json";

interface BackupMetadata {
  version: string;
  exportDate: string;
  collections: string[];
  totalRecords: number;
}

interface BackupData {
  metadata: BackupMetadata;
  data: Record<string, any[]>;
}

interface ImportResult {
  success: boolean;
  message: string;
  importedRecords?: number;
  skippedCollections?: string[];
}

interface ExportResult {
  success: boolean;
  message: string;
  filePath?: string;
  exportedRecords?: number;
}

@injectable()
@ServiceRegister(TYPES.DatabaseBackupService)
export class DatabaseBackupService {
  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__TWO_WAY)
  async exportDatabase(_event: Electron.IpcMainEvent): Promise<ExportResult> {
    try {
      logger.info("Starting database export...");

      // Show save dialog
      const { filePath, canceled } = await dialog.showSaveDialog({
        title: "Export Database",
        defaultPath: `302ai-studio-backup-${new Date().toISOString().split("T")[0]}.json`,
        filters: [
          { name: "JSON Files", extensions: ["json"] },
          { name: "All Files", extensions: ["*"] },
        ],
      });

      if (canceled || !filePath) {
        return { success: false, message: "Export cancelled" };
      }

      // Get all collection names from schema
      const collectionNames = Object.keys(schema) as CollectionNameFromModels<
        typeof schema
      >[];

      const exportData: BackupData = {
        metadata: {
          version: packageJSON.version,
          exportDate: new Date().toISOString(),
          collections: collectionNames,
          totalRecords: 0,
        },
        data: {},
      };

      let totalRecords = 0;

      // Export each collection
      for (const collectionName of collectionNames) {
        try {
          const query = (triplitClient as any).query(collectionName);
          const result = await (triplitClient as any).fetch(query, {
            policy: "local-first",
          });

          const records: any[] = [];
          if (result && typeof result.values === "function") {
            for (const record of result.values()) {
              // Create a serializable copy of the record
              const serializedRecord = this.serializeRecord(
                record,
                collectionName,
              );
              records.push(serializedRecord);
            }
          }

          exportData.data[collectionName] = records;
          totalRecords += records.length;

          logger.info(
            `Exported ${records.length} records from ${collectionName}`,
          );
        } catch (error) {
          logger.warn(`Failed to export collection ${collectionName}:`, {
            error,
          });
          exportData.data[collectionName] = [];
        }
      }

      exportData.metadata.totalRecords = totalRecords;

      // Write to file
      await fs.promises.writeFile(
        filePath,
        JSON.stringify(exportData, null, 2),
        "utf8",
      );

      logger.info(
        `Database export completed. ${totalRecords} records exported to ${filePath}`,
      );

      return {
        success: true,
        message: `Database exported successfully! ${totalRecords} records exported`,
        filePath,
        exportedRecords: totalRecords,
      };
    } catch (error) {
      const errorMessage = extractErrorMessage(error);
      logger.error("Database export failed:", { error: errorMessage });
      return {
        success: false,
        message: `Export failed: ${errorMessage}`,
      };
    }
  }

  private serializeRecord(record: any, collectionName: string): any {
    const serialized = { ...record };

    // Handle special field types based on collection
    if (collectionName === "shortcuts" && record.keys) {
      // Convert Set to Array for JSON serialization
      if (record.keys instanceof Set) {
        serialized.keys = Array.from(record.keys);
      } else if (Array.isArray(record.keys)) {
        serialized.keys = [...record.keys];
      }
    }

    // Handle Date objects
    for (const [key, value] of Object.entries(serialized)) {
      if (value instanceof Date) {
        serialized[key] = value.toISOString();
      } else if (value instanceof Set) {
        serialized[key] = Array.from(value);
      }
    }

    return serialized;
  }

  private deserializeRecord(record: any, collectionName: string): any {
    const deserialized = { ...record };

    // Handle special field types based on collection
    if (collectionName === "shortcuts" && record.keys) {
      // Convert Array back to Set for Triplit
      if (Array.isArray(record.keys)) {
        deserialized.keys = new Set(record.keys);
      }
    }

    // Handle Date fields
    const dateFields = ["createdAt", "updatedAt"];
    for (const field of dateFields) {
      if (deserialized[field] && typeof deserialized[field] === "string") {
        deserialized[field] = new Date(deserialized[field]);
      }
    }

    return deserialized;
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__TWO_WAY)
  async importDatabase(_event: Electron.IpcMainEvent): Promise<ImportResult> {
    try {
      logger.info("Starting database import...");

      // Show open dialog
      const { filePaths, canceled } = await dialog.showOpenDialog({
        title: "Import Database",
        filters: [
          { name: "JSON Files", extensions: ["json"] },
          { name: "All Files", extensions: ["*"] },
        ],
        properties: ["openFile"],
      });

      if (canceled || filePaths.length === 0) {
        return { success: false, message: "Import cancelled" };
      }

      const filePath = filePaths[0];

      // Read and parse backup file
      const fileContent = await fs.promises.readFile(filePath, "utf8");
      let backupData: BackupData;

      try {
        backupData = JSON.parse(fileContent);
      } catch (_parseError) {
        return { success: false, message: "Invalid backup file format" };
      }

      // Validate backup data structure
      if (!backupData.metadata || !backupData.data) {
        return { success: false, message: "Invalid backup file structure" };
      }

      // Check version compatibility
      const compatibilityResult = this.checkVersionCompatibility(
        backupData.metadata.version,
      );

      if (!compatibilityResult.compatible) {
        return {
          success: false,
          message: `Version incompatible: ${compatibilityResult.message}`,
        };
      }

      // Import data
      let importedRecords = 0;
      const skippedCollections: string[] = [];

      for (const [collectionName, records] of Object.entries(backupData.data)) {
        try {
          if (!Array.isArray(records)) {
            logger.warn(`Skipping invalid collection data: ${collectionName}`);
            skippedCollections.push(collectionName);
            continue;
          }

          // Clear existing data in collection (optional - could be made configurable)
          const existingQuery = (triplitClient as any).query(collectionName);
          const existingData = await (triplitClient as any).fetch(
            existingQuery,
            {
              policy: "local-first",
            },
          );

          if (existingData && typeof existingData.values === "function") {
            for (const record of existingData.values()) {
              if (record && typeof record === "object" && "id" in record) {
                await (triplitClient as any).delete(collectionName, record.id);
              }
            }
          }

          // Import new data
          for (const record of records) {
            if (record && typeof record === "object" && record.id) {
              const deserializedRecord = this.deserializeRecord(
                record,
                collectionName,
              );
              await (triplitClient as any).insert(
                collectionName,
                deserializedRecord,
              );
              importedRecords++;
            }
          }

          logger.info(
            `Imported ${records.length} records to ${collectionName}`,
          );
        } catch (error) {
          logger.warn(`Failed to import collection ${collectionName}:`, {
            error,
          });
          skippedCollections.push(collectionName);
        }
      }

      logger.info(
        `Database import completed. ${importedRecords} records imported`,
      );

      return {
        success: true,
        message: `Database imported successfully! ${importedRecords} records imported`,
        importedRecords,
        skippedCollections,
      };
    } catch (error) {
      const errorMessage = extractErrorMessage(error);
      logger.error("Database import failed:", { error: errorMessage });
      return {
        success: false,
        message: `Import failed: ${errorMessage}`,
      };
    }
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__TWO_WAY)
  async validateBackupFile(
    _event: Electron.IpcMainEvent,
    filePath: string,
  ): Promise<{
    valid: boolean;
    message: string;
    metadata?: BackupMetadata;
    compatible?: boolean;
  }> {
    try {
      const fileContent = await fs.promises.readFile(filePath, "utf8");
      const backupData: BackupData = JSON.parse(fileContent);

      if (!backupData.metadata || !backupData.data) {
        return { valid: false, message: "Invalid backup file structure" };
      }

      const compatibilityResult = this.checkVersionCompatibility(
        backupData.metadata.version,
      );

      return {
        valid: true,
        message: "Valid backup file",
        metadata: backupData.metadata,
        compatible: compatibilityResult.compatible,
      };
    } catch (_error) {
      return { valid: false, message: "Invalid or corrupted backup file" };
    }
  }

  private checkVersionCompatibility(backupVersion: string): {
    compatible: boolean;
    message: string;
  } {
    const currentVersion = packageJSON.version;

    try {
      // Allow imports from same or older versions
      if (semver.gte(currentVersion, backupVersion)) {
        return {
          compatible: true,
          message: "Version compatible",
        };
      }

      // Warn about newer versions but still allow
      if (semver.gt(backupVersion, currentVersion)) {
        return {
          compatible: true,
          message: `Warning: Backup from newer version (${backupVersion}), current version (${currentVersion})`,
        };
      }
    } catch (error) {
      logger.warn("Version comparison failed:", { error });
    }

    return {
      compatible: false,
      message: `Version incompatible: backup version ${backupVersion}, current version ${currentVersion}`,
    };
  }

  @ServiceHandler(CommunicationWay.RENDERER_TO_MAIN__TWO_WAY)
  async getBackupInfo(_event: Electron.IpcMainEvent): Promise<{
    currentVersion: string;
    supportedFormats: string[];
  }> {
    return {
      currentVersion: packageJSON.version,
      supportedFormats: ["json"],
    };
  }
}
